-- SQL Profile  -- chargement à partir d'un plan d'exécution du Library Cache
-- d'après le blog de Kerry Osborne ( http://kerryosborne.oracle-guy.com/2009/04/oracle-sql-profiles/ )
-- et un exemple de Christian Antognini ( http://antognini.ch/top/ )

DROP TABLE t1;

CREATE TABLE t1 (id, col1, col2, pad)
AS 
SELECT rownum, CASE WHEN rownum>5000 THEN 5000 ELSE rownum END, rownum, lpad('*',100,'*')
FROM dual
CONNECT BY level <= 10000;

CREATE INDEX t1_col1 ON t1 (col1);

-- Calcul des statistiques avec des histogrammes automatiques
BEGIN
 dbms_stats.gather_table_stats(
   ownname=>user, 
   tabname=>'T1', 
   cascade=>TRUE,
   estimate_percent=>100,
   method_opt=>'for all columns size skewonly',
   no_invalidate=>FALSE);
END;
/

-- Du moment que cursor_sharing n'est pas FORCE, des plans d'exécution differents sont
-- générés en foction de la valeur du litteral
-- On peut vérifier avec une 2ème session SYSDBA et les requêtes suivantes:
-- SQL> select sid,serial#,SQL_ID,SQL_CHILD_NUMBER,PREV_SQL_ID,PREV_CHILD_NUMBER  from v$session where username=&username;
-- SQL> select * from table(dbms_xplan.display_cursor(&SQL_ID,&SQL_CHILD_NUMBER,'typical'));
-- probablement  &PREV_SQL_ID et &PREV_CHILD_NUMBER car les requêtes sont rapides


-------------------------------------
-- Dans une 2ème session AS SYSDBA =>
-- nettoyage du Library Cache
alter system flush shared_pool;
-------------------------------------
-- Session applicative => 
-- FULL SCAN
select * from t1 where col1=5000;

-- Dans une 2ème session AS SYSDBA =>
-- 1er snapshoot AWR
execute dbms_workload_repository.CREATE_SNAPSHOT;


-- Session applicative => 
-- ACCES PAR INDEX
select * from t1 where col1=128;


-- Dans une 2ème session AS SYSDBA =>
-- 2ème snapshoot AWR
execute dbms_workload_repository.CREATE_SNAPSHOT;

------------------------------------------------------
-- SESSION 2 as SYSDBA
-- on récupère le SQL_ID, ainsi que le Plan hash value du SQL qui fait en FULL 

set pages 999
set line 200
col SQL_TEXT for a70 wrap

select dbid,sql_id,sql_text from DBA_HIST_SQLTEXT where SQL_TEXT like '%select * from t1 where col1=%';

select * from table(dbms_xplan.display_awr('b9tum9b80gsjx')); -- FULL

-- Nous alons créer un SQL Profile qui utilise ce denier plan d'exécution (FULL)
-- les valeurs d'entrée:
-- sql_id = 'b9tum9b80gsjx' / plan_hash_value=3617692013 / category => 'SQLPROF_CAT_01' / name => 'SQLPROF_SQLPROF_02' / force_match => true

/* Randolf Giest */
-- create sql profile from awr
-- sql_id plan_hash_valeu category force_matching
declare
  ar_profile_hints sys.sqlprof_attr;
  cl_sql_text clob;
begin
  select
  extractvalue(value(d), '/hint') as outline_hints
  bulk collect
  into
  ar_profile_hints
  from
  xmltable('/*/outline_data/hint'
  passing (
  select
  xmltype(other_xml) as xmlval
  from
  dba_hist_sql_plan
  where
  sql_id = 'b9tum9b80gsjx'
  and plan_hash_value = 3617692013
  and other_xml is not null
  )
  ) d;
  
  select
  sql_text
  into
  cl_sql_text
  from
  dba_hist_sqltext
  where
  sql_id = 'b9tum9b80gsjx';
  
  dbms_sqltune.import_sql_profile(
  sql_text => cl_sql_text
  , profile => ar_profile_hints
  , category => 'SQLPROF_CAT_01'
  , name => 'SQLPROF_SQLPROF_02'
  -- use force_match => true
  -- to use CURSOR_SHARING=SIMILAR
  -- behaviour, i.e. match even with
  -- differing literals
  , force_match => true
  );
end;
/

-- On vérifie que le SQL Plan a été créé
-- normallement c'est afficher dans la note

set line 128
col sql_text for a40 wrap
select name,category,sql_text,status,force_matching from DBA_SQL_PROFILES;

------------------------------------------------------
-- On reviens dans la première session (celle applicative)
-- on change la catègorie courante de SQL Tune
-- on vérifie que le pla d'exécution est bien celui du SQL Profile

alter session set sqltune_category='SQLPROF_CAT_01';

explain plan for select * from t1 where col1=5000;
select * from table(dbms_xplan.display);

explain plan for select * from t1 where col1=1200;
select * from table(dbms_xplan.display);
